package com.toutiao.score

import scala.collection.mutable.{HashMap => HMap}

import scala.math.{log,min,max,sqrt}

import org.apache.log4j.Logger

class ScoreFeaturer(val M: Map[String, Int], val N: Int) {

  private val MIN_CC = 5
  
  private val AVGDL = 500
  private val K1 = 1.7
  private val B = 0.75
  
  private val BOOST_TITLE = 3

  val _log = Logger.getLogger(this.getClass.getName)
  
  def cut(line:String) = {
    val vs = line.
      replaceAll("\n", " ").
      replaceAll("[\\pP]", " ").
      replaceAll("\\p{IsZs}+", " ").toLowerCase.
      split(" ")    
    vs
  }

  def getTokenCount(cutTitle: String, cutContent: String) = {
    val vs = ((cutTitle+" ")*BOOST_TITLE + " " + cutContent).
      replaceAll("\n", " ").
      replaceAll("[\\pP]", " ").
      replaceAll("\\p{IsZs}+", " ").toLowerCase.
      split(" ")
    val m = HMap[String, Int]()
    for (v <- vs) {
      val c = m.getOrElse(v, 0)
      m(v) = c + 1
    }

    m.toMap
  }

  // Bm25
  def getBm25Score(cutTitle:String, cutContent:String) = {
    val m = getTokenCount(cutTitle,cutContent)
    val count = (m.values++Array(0)).reduce(_+_) // 会有内容为空的Doc
    m.map(vc => {
      val v = vc._1
      val c = vc._2
      val n = M.getOrElse(v,MIN_CC)
      
      val w1 = log((N-n+0.5)/(n+0.5))
      val w2 = c*(K1+1)/(c+K1*(1-B+B*count/AVGDL))
      
      (v,w1*w2)
    })    
  }
  
  // TFIDF
  def getTFIDFScore(cutTitle:String, cutContent:String) = {
    val vm = getTokenCount(cutTitle,cutContent)
    val s = vm.map(v => {
      val ss =
        v._2*log(N/M.getOrElse(v._1, MIN_CC))
      (v._1,ss)
    })
    s.toMap    
  }
  
  // TitleScore
  def getTitleScore(vs:Array[String],vm:Map[String,Double],maxV:Double=30.0) = {
    vs.map(v => {
      (v, min(maxV,vm.getOrElse(v,2.0)))
    }).toMap
  }
  
  def norm(vs:Map[String,Double],n:Int = 30) = {
    val maxV = 120.0
    val minV = 3.0
    val x = 6.0
    val maxU = 0.8
    
    val us = vs.filter(_._2 > minV).filter(_._1.size > 1).toArray.
    			map(vv => {
    			  if (vv._2 > maxV) (vv._1,maxV)
    			  else vv
    			}).
    			sortBy(-_._2)
    val cnt = if (us.size > n) n else us.size
    val vvs = us.slice(0,cnt).toMap
    val s = if (vvs.size < n){
      vvs.values.map(v => v*v).sum + (n - vvs.size)*x*x
    } else {
      vvs.values.map(v => v*v).sum
    }
    val ss = sqrt(s)
    vvs.map(vv => {
      (vv._1,min(maxU,vv._2/ss))
    })
   
  }
  
  def score1(cutTitle:String, cutContent:String) = {
    val m = getBm25Score(cutTitle,cutContent)
    m
  }
  
  def score2(cutTitle:String, cutContent:String) = {
    val m = getTFIDFScore(cutTitle,cutContent)
    m
  }
  
  def score3(cutTitle:String,cutContent:String) = {
    val m = getBm25Score(cutTitle,cutContent)
    val vvs = cutTitle.replaceAll("\n"," ").
    			replaceAll("[\\pP]", " ").
    			replaceAll("\\p{IsZs}+", " ").toLowerCase.
    			split(" ")
    getTitleScore(vvs,m,30.0)    
  }
  
  def score4(cutTitle:String,cutContent:String) = {
    val m = getTFIDFScore(cutTitle,cutContent)
    val vvs = cutTitle.replaceAll("\n"," ").
    			replaceAll("[\\pP]", " ").
    			replaceAll("\\p{IsZs}+", " ").toLowerCase.
    			split(" ")
    getTitleScore(vvs,m,30.0)
  }
  


}